{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "rNdWfPXCjTjY"
   },
   "source": [
    "# Performing Basic Feature Engineering in Keras \n",
    "\n",
    "## Learning objectives\n",
    "\n",
    "\n",
    "1. Create an input pipeline using tf.data.\n",
    "2. Engineer features to create categorical, crossed, and numerical feature columns.\n",
    "\n",
    "\n",
    "## Introduction \n",
    "In this lab, we utilize feature engineering to improve the prediction of housing prices using a Keras Sequential Model.\n",
    "\n",
    "Each learning objective will correspond to a __#TODO__ in the notebook where you will complete the notebook cell's code before running. Refer to the [solution](../solutions/3_keras_basic_feat_eng.ipynb) for reference. \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "VxyBFc_kKazA"
   },
   "source": [
    "Start by importing the necessary libraries for this lab."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "!sudo chown -R jupyter:jupyter /home/jupyter/training-data-analyst"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Collecting sklearn\n",
      "  Downloading sklearn-0.0.tar.gz (1.1 kB)\n",
      "  Preparing metadata (setup.py) ... \u001b[?25ldone\n",
      "\u001b[?25hRequirement already satisfied: scikit-learn in /opt/conda/lib/python3.7/site-packages (from sklearn) (1.0.1)\n",
      "Requirement already satisfied: scipy>=1.1.0 in /opt/conda/lib/python3.7/site-packages (from scikit-learn->sklearn) (1.7.3)\n",
      "Requirement already satisfied: threadpoolctl>=2.0.0 in /opt/conda/lib/python3.7/site-packages (from scikit-learn->sklearn) (3.0.0)\n",
      "Requirement already satisfied: joblib>=0.11 in /opt/conda/lib/python3.7/site-packages (from scikit-learn->sklearn) (1.1.0)\n",
      "Requirement already satisfied: numpy>=1.14.6 in /opt/conda/lib/python3.7/site-packages (from scikit-learn->sklearn) (1.19.5)\n",
      "Building wheels for collected packages: sklearn\n",
      "  Building wheel for sklearn (setup.py) ... \u001b[?25ldone\n",
      "\u001b[?25h  Created wheel for sklearn: filename=sklearn-0.0-py2.py3-none-any.whl size=1309 sha256=3eb25fc76a7a56b67820f445014850e58a9deb2d22d54ab34215dca48a304bfe\n",
      "  Stored in directory: /home/jupyter/.cache/pip/wheels/46/ef/c3/157e41f5ee1372d1be90b09f74f82b10e391eaacca8f22d33e\n",
      "Successfully built sklearn\n",
      "Installing collected packages: sklearn\n",
      "Successfully installed sklearn-0.0\n"
     ]
    }
   ],
   "source": [
    "# Install Sklearn\n",
    "!python3 -m pip install --user scikit-learn"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "hhq5zEbGg0XX"
   },
   "source": [
    "### Restart the kernel\n",
    "\n",
    "After you install the packages, you need to restart the notebook kernel so that it can find the packages. (Click **Kernel > Restart Kernel > Restart**)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "9dEreb4QKizj"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "TensorFlow version:  2.6.5\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "import tensorflow.keras\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "import pandas as pd\n",
    "import tensorflow as tf\n",
    "\n",
    "from tensorflow import feature_column as fc\n",
    "from tensorflow.keras import layers\n",
    "from sklearn.model_selection import train_test_split\n",
    "#from keras.utils import plot_model\n",
    "\n",
    "print(\"TensorFlow version: \",tf.version.VERSION)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Many of the Google Machine Learning Courses Programming Exercises use the  [California Housing Dataset](https://developers.google.com/machine-learning/crash-course/california-housing-data-description\n",
    "), which contains data drawn from the 1990 U.S. Census.  Our lab dataset has been pre-processed so that there are no missing values.\n",
    "\n",
    "First, let's download the raw .csv data by copying the data from a cloud storage bucket.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "if not os.path.isdir(\"../data\"):\n",
    "    os.makedirs(\"../data\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Copying gs://cloud-training/mlongcp/v3.0_MLonGC/toy_data/housing_pre-proc_toy.csv...\n",
      "/ [1 files][138.8 KiB/138.8 KiB]                                                \n",
      "Operation completed over 1 objects/138.8 KiB.                                    \n"
     ]
    }
   ],
   "source": [
    "!gcloud storage cp gs://cloud-training/mlongcp/v3.0_MLonGC/toy_data/housing_pre-proc_toy.csv ../data    "   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "total 140\n",
      "-rw-r--r-- 1 jupyter jupyter 142150 Dec 27 07:25 housing_pre-proc_toy.csv\n"
     ]
    }
   ],
   "source": [
    "!ls -l ../data/"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "lM6-n6xntv3t"
   },
   "source": [
    "Now, let's read in the dataset just copied from the cloud storage bucket and create a Pandas dataframe."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 222
    },
    "colab_type": "code",
    "id": "REZ57BXCLdfG",
    "outputId": "a6ef2eda-c7eb-4e2d-92e4-e7fcaa20b0af"
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/conda/lib/python3.7/site-packages/IPython/core/interactiveshell.py:3457: FutureWarning: The error_bad_lines argument has been deprecated and will be removed in a future version.\n",
      "\n",
      "\n",
      "  exec(code_obj, self.user_global_ns, self.user_ns)\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>longitude</th>\n",
       "      <th>latitude</th>\n",
       "      <th>housing_median_age</th>\n",
       "      <th>total_rooms</th>\n",
       "      <th>total_bedrooms</th>\n",
       "      <th>population</th>\n",
       "      <th>households</th>\n",
       "      <th>median_income</th>\n",
       "      <th>median_house_value</th>\n",
       "      <th>ocean_proximity</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>-122.23</td>\n",
       "      <td>37.88</td>\n",
       "      <td>41</td>\n",
       "      <td>880</td>\n",
       "      <td>129</td>\n",
       "      <td>322</td>\n",
       "      <td>126</td>\n",
       "      <td>8.3252</td>\n",
       "      <td>452600</td>\n",
       "      <td>NEAR BAY</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>-122.22</td>\n",
       "      <td>37.86</td>\n",
       "      <td>21</td>\n",
       "      <td>7099</td>\n",
       "      <td>1106</td>\n",
       "      <td>2401</td>\n",
       "      <td>1138</td>\n",
       "      <td>8.3014</td>\n",
       "      <td>358500</td>\n",
       "      <td>NEAR BAY</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>-122.24</td>\n",
       "      <td>37.85</td>\n",
       "      <td>52</td>\n",
       "      <td>1467</td>\n",
       "      <td>190</td>\n",
       "      <td>496</td>\n",
       "      <td>177</td>\n",
       "      <td>7.2574</td>\n",
       "      <td>352100</td>\n",
       "      <td>NEAR BAY</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>-122.25</td>\n",
       "      <td>37.85</td>\n",
       "      <td>52</td>\n",
       "      <td>1274</td>\n",
       "      <td>235</td>\n",
       "      <td>558</td>\n",
       "      <td>219</td>\n",
       "      <td>5.6431</td>\n",
       "      <td>341300</td>\n",
       "      <td>NEAR BAY</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>-122.25</td>\n",
       "      <td>37.85</td>\n",
       "      <td>52</td>\n",
       "      <td>1627</td>\n",
       "      <td>280</td>\n",
       "      <td>565</td>\n",
       "      <td>259</td>\n",
       "      <td>3.8462</td>\n",
       "      <td>342200</td>\n",
       "      <td>NEAR BAY</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   longitude  latitude  housing_median_age  total_rooms  total_bedrooms  \\\n",
       "0    -122.23     37.88                  41          880             129   \n",
       "1    -122.22     37.86                  21         7099            1106   \n",
       "2    -122.24     37.85                  52         1467             190   \n",
       "3    -122.25     37.85                  52         1274             235   \n",
       "4    -122.25     37.85                  52         1627             280   \n",
       "\n",
       "   population  households  median_income  median_house_value ocean_proximity  \n",
       "0         322         126         8.3252              452600        NEAR BAY  \n",
       "1        2401        1138         8.3014              358500        NEAR BAY  \n",
       "2         496         177         7.2574              352100        NEAR BAY  \n",
       "3         558         219         5.6431              341300        NEAR BAY  \n",
       "4         565         259         3.8462              342200        NEAR BAY  "
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "housing_df = pd.read_csv('../data/housing_pre-proc_toy.csv', on_bad_lines='skip')\n",
    "housing_df.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can use .describe() to see some summary statistics for the numeric fields in our dataframe. Note, for example, the count row and corresponding columns. The count shows 2500.000000 for all feature columns. Thus, there are no missing values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>longitude</th>\n",
       "      <th>latitude</th>\n",
       "      <th>housing_median_age</th>\n",
       "      <th>total_rooms</th>\n",
       "      <th>total_bedrooms</th>\n",
       "      <th>population</th>\n",
       "      <th>households</th>\n",
       "      <th>median_income</th>\n",
       "      <th>median_house_value</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>count</th>\n",
       "      <td>2500.000000</td>\n",
       "      <td>2500.000000</td>\n",
       "      <td>2500.000000</td>\n",
       "      <td>2500.000000</td>\n",
       "      <td>2500.000000</td>\n",
       "      <td>2500.000000</td>\n",
       "      <td>2500.000000</td>\n",
       "      <td>2500.000000</td>\n",
       "      <td>2500.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>mean</th>\n",
       "      <td>-121.501836</td>\n",
       "      <td>37.802288</td>\n",
       "      <td>30.088400</td>\n",
       "      <td>2522.734000</td>\n",
       "      <td>491.862400</td>\n",
       "      <td>1246.225200</td>\n",
       "      <td>458.122000</td>\n",
       "      <td>3.694312</td>\n",
       "      <td>170288.731200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>std</th>\n",
       "      <td>1.015963</td>\n",
       "      <td>0.803090</td>\n",
       "      <td>13.878416</td>\n",
       "      <td>1988.411988</td>\n",
       "      <td>362.499497</td>\n",
       "      <td>925.075463</td>\n",
       "      <td>341.744308</td>\n",
       "      <td>1.859422</td>\n",
       "      <td>97550.278529</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>min</th>\n",
       "      <td>-124.300000</td>\n",
       "      <td>36.130000</td>\n",
       "      <td>2.000000</td>\n",
       "      <td>12.000000</td>\n",
       "      <td>4.000000</td>\n",
       "      <td>18.000000</td>\n",
       "      <td>2.000000</td>\n",
       "      <td>0.499900</td>\n",
       "      <td>22500.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25%</th>\n",
       "      <td>-122.200000</td>\n",
       "      <td>37.600000</td>\n",
       "      <td>18.000000</td>\n",
       "      <td>1420.750000</td>\n",
       "      <td>282.000000</td>\n",
       "      <td>718.000000</td>\n",
       "      <td>263.000000</td>\n",
       "      <td>2.357875</td>\n",
       "      <td>92950.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>50%</th>\n",
       "      <td>-122.030000</td>\n",
       "      <td>37.800000</td>\n",
       "      <td>30.000000</td>\n",
       "      <td>2052.000000</td>\n",
       "      <td>402.000000</td>\n",
       "      <td>1030.500000</td>\n",
       "      <td>374.500000</td>\n",
       "      <td>3.262200</td>\n",
       "      <td>150800.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>75%</th>\n",
       "      <td>-120.697500</td>\n",
       "      <td>37.960000</td>\n",
       "      <td>41.000000</td>\n",
       "      <td>3007.250000</td>\n",
       "      <td>581.250000</td>\n",
       "      <td>1488.250000</td>\n",
       "      <td>538.000000</td>\n",
       "      <td>4.662975</td>\n",
       "      <td>219650.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>max</th>\n",
       "      <td>-118.910000</td>\n",
       "      <td>41.950000</td>\n",
       "      <td>52.000000</td>\n",
       "      <td>28258.000000</td>\n",
       "      <td>3864.000000</td>\n",
       "      <td>12203.000000</td>\n",
       "      <td>3701.000000</td>\n",
       "      <td>15.000100</td>\n",
       "      <td>500001.000000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "         longitude     latitude  housing_median_age   total_rooms  \\\n",
       "count  2500.000000  2500.000000         2500.000000   2500.000000   \n",
       "mean   -121.501836    37.802288           30.088400   2522.734000   \n",
       "std       1.015963     0.803090           13.878416   1988.411988   \n",
       "min    -124.300000    36.130000            2.000000     12.000000   \n",
       "25%    -122.200000    37.600000           18.000000   1420.750000   \n",
       "50%    -122.030000    37.800000           30.000000   2052.000000   \n",
       "75%    -120.697500    37.960000           41.000000   3007.250000   \n",
       "max    -118.910000    41.950000           52.000000  28258.000000   \n",
       "\n",
       "       total_bedrooms    population   households  median_income  \\\n",
       "count     2500.000000   2500.000000  2500.000000    2500.000000   \n",
       "mean       491.862400   1246.225200   458.122000       3.694312   \n",
       "std        362.499497    925.075463   341.744308       1.859422   \n",
       "min          4.000000     18.000000     2.000000       0.499900   \n",
       "25%        282.000000    718.000000   263.000000       2.357875   \n",
       "50%        402.000000   1030.500000   374.500000       3.262200   \n",
       "75%        581.250000   1488.250000   538.000000       4.662975   \n",
       "max       3864.000000  12203.000000  3701.000000      15.000100   \n",
       "\n",
       "       median_house_value  \n",
       "count         2500.000000  \n",
       "mean        170288.731200  \n",
       "std          97550.278529  \n",
       "min          22500.000000  \n",
       "25%          92950.000000  \n",
       "50%         150800.000000  \n",
       "75%         219650.000000  \n",
       "max         500001.000000  "
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "housing_df.describe()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "u0zhLtQqMPem"
   },
   "source": [
    "####  Split the dataset for ML\n",
    "\n",
    "The dataset we loaded was a single CSV file. We will split this into train, validation, and test sets.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 69
    },
    "colab_type": "code",
    "id": "YEOpw7LhMYsI",
    "outputId": "6161a660-7133-465a-d754-d7acae2b68c8"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1600 train examples\n",
      "400 validation examples\n",
      "500 test examples\n"
     ]
    }
   ],
   "source": [
    "train, test = train_test_split(housing_df, test_size=0.2)\n",
    "train, val = train_test_split(train, test_size=0.2)\n",
    "\n",
    "print(len(train), 'train examples')\n",
    "print(len(val), 'validation examples')\n",
    "print(len(test), 'test examples')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "dz9kfjOMBX9U"
   },
   "source": [
    "Now, we need to output the split files.  We will specifically need the test.csv later for testing.  You should see the files appear in the home directory.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 222
    },
    "colab_type": "code",
    "id": "ADX23QUu_Wiu",
    "outputId": "e97fa59e-4ed4-48a3-8fba-c95f293944ee"
   },
   "outputs": [],
   "source": [
    "train.to_csv('../data/housing-train.csv', encoding='utf-8', index=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "val.to_csv('../data/housing-val.csv', encoding='utf-8', index=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 222
    },
    "colab_type": "code",
    "id": "CU1FgmKEAmWh",
    "outputId": "2cce91e1-2c4a-4fe8-a6c3-3da52cb9458f"
   },
   "outputs": [],
   "source": [
    "test.to_csv('../data/housing-test.csv', encoding='utf-8', index=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==> ../data/housing-test.csv <==\n",
      "longitude,latitude,housing_median_age,total_rooms,total_bedrooms,population,households,median_income,median_house_value,ocean_proximity\n",
      "-119.46,36.61,13,1348,258,719,246,3.625,108300,INLAND\n",
      "-119.65,36.51,30,1671,319,966,282,3.1333,100000,INLAND\n",
      "-121.6,37.9,5,14684,2252,4276,1722,6.9051,340900,INLAND\n",
      "-121.82,38.0,29,2070,452,985,420,2.8466,113400,INLAND\n",
      "-121.76,37.67,6,3023,518,1225,468,6.3705,350000,INLAND\n",
      "-121.64,37.85,22,1999,415,967,320,4.4583,253900,INLAND\n",
      "-122.22,37.78,50,1920,530,1525,477,1.4886,128800,NEAR BAY\n",
      "-121.6,39.79,18,2672,533,1151,532,2.567,102900,INLAND\n",
      "-122.19,37.71,36,361,69,158,58,5.5461,262500,NEAR BAY\n",
      "\n",
      "==> ../data/housing-train.csv <==\n",
      "longitude,latitude,housing_median_age,total_rooms,total_bedrooms,population,households,median_income,median_house_value,ocean_proximity\n",
      "-119.99,38.93,23,1882,414,673,277,2.9091,141900,INLAND\n",
      "-122.02,37.63,6,2445,590,1189,573,3.8958,301100,NEAR BAY\n",
      "-122.28,37.8,38,684,176,344,155,2.0114,131300,NEAR BAY\n",
      "-120.65,38.5,10,1783,337,638,262,2.65,116700,INLAND\n",
      "-122.28,37.9,52,2318,328,779,312,7.1754,362900,NEAR BAY\n",
      "-120.98,38.34,27,3471,653,1793,600,3.5508,99100,INLAND\n",
      "-122.02,37.88,16,3031,438,1087,421,7.3732,287300,NEAR BAY\n",
      "-121.55,39.51,50,1050,288,485,260,1.1607,51700,INLAND\n",
      "-120.45,36.86,34,673,173,539,182,2.3523,66000,INLAND\n",
      "\n",
      "==> ../data/housing-val.csv <==\n",
      "longitude,latitude,housing_median_age,total_rooms,total_bedrooms,population,households,median_income,median_house_value,ocean_proximity\n",
      "-120.43,38.25,13,763,161,311,125,2.4583,112500,INLAND\n",
      "-122.12,37.69,30,1197,269,695,279,3.4375,157800,NEAR BAY\n",
      "-122.09,37.98,14,5381,871,2296,872,5.6875,211000,NEAR BAY\n",
      "-122.21,37.78,49,898,244,779,245,3.0536,137500,NEAR BAY\n",
      "-119.92,38.91,15,3831,625,984,328,5.0718,162500,INLAND\n",
      "-121.91,38.02,15,2966,558,1687,527,3.4817,129800,INLAND\n",
      "-122.14,37.85,27,9147,1276,3371,1269,7.3267,389900,NEAR BAY\n",
      "-122.23,37.78,44,2340,825,2813,751,1.6009,118100,NEAR BAY\n",
      "-122.05,37.87,30,2296,329,847,322,6.7192,397500,NEAR BAY\n",
      "\n",
      "==> ../data/housing_pre-proc_toy.csv <==\n",
      "longitude,latitude,housing_median_age,total_rooms,total_bedrooms,population,households,median_income,median_house_value,ocean_proximity\n",
      "-122.23,37.88,41,880,129,322,126,8.3252,452600,NEAR BAY\n",
      "-122.22,37.86,21,7099,1106,2401,1138,8.3014,358500,NEAR BAY\n",
      "-122.24,37.85,52,1467,190,496,177,7.2574,352100,NEAR BAY\n",
      "-122.25,37.85,52,1274,235,558,219,5.6431,341300,NEAR BAY\n",
      "-122.25,37.85,52,1627,280,565,259,3.8462,342200,NEAR BAY\n",
      "-122.25,37.85,52,919,213,413,193,4.0368,269700,NEAR BAY\n",
      "-122.25,37.84,52,2535,489,1094,514,3.6591,299200,NEAR BAY\n",
      "-122.25,37.84,52,3104,687,1157,647,3.12,241400,NEAR BAY\n",
      "-122.26,37.84,42,2555,665,1206,595,2.0804,226700,NEAR BAY\n"
     ]
    }
   ],
   "source": [
    "!head ../data/housing*.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Aj35eYy_lutI"
   },
   "source": [
    "## Lab Task 1: Create an input pipeline using tf.data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "84ef46LXMfvu"
   },
   "source": [
    "Next, we will wrap the dataframes with [tf.data](https://www.tensorflow.org/guide/datasets). This will enable us  to use feature columns as a bridge to map from the columns in the Pandas dataframe to features used to train the model. \n",
    "\n",
    "Here, we create an input pipeline using tf.data.  This function is missing two lines.  Correct and run the cell."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "# A utility method to create a tf.data dataset from a Pandas Dataframe\n",
    "\n",
    "def df_to_dataset(dataframe, shuffle=True, batch_size=32):\n",
    "    dataframe = dataframe.copy()\n",
    "    \n",
    "   # TODO 1a -- Your code here\n",
    "\n",
    "    if shuffle:\n",
    "        ds = ds.shuffle(buffer_size=len(dataframe))\n",
    "    ds = ds.batch(batch_size)\n",
    "    return ds"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next we initialize the training and validation datasets."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "batch_size = 32\n",
    "train_ds = df_to_dataset(train)\n",
    "val_ds = df_to_dataset(val, shuffle=False, batch_size=batch_size)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "qRLGSMDzM-dl"
   },
   "source": [
    "Now that we have created the input pipeline, let's call it to see the format of the data it returns. We have used a small batch size to keep the output readable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 331
    },
    "colab_type": "code",
    "id": "CSBo3dUVNFc9",
    "outputId": "d1be2646-b1e5-4110-dbba-5bc49d9b30f6"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Every feature: ['longitude', 'latitude', 'housing_median_age', 'total_rooms', 'total_bedrooms', 'population', 'households', 'median_income', 'ocean_proximity']\n",
      "A batch of households: tf.Tensor(\n",
      "[ 994  540  326  593  102  265  239  351  721  728  556  462  229  369\n",
      " 1289  123  134  314  566  535  232  520  888  286   81  281  632  302\n",
      "  340  924  259  363], shape=(32,), dtype=int64)\n",
      "A batch of ocean_proximity: tf.Tensor(\n",
      "[b'<1H OCEAN' b'NEAR BAY' b'NEAR BAY' b'NEAR BAY' b'INLAND' b'INLAND'\n",
      " b'NEAR BAY' b'INLAND' b'NEAR OCEAN' b'NEAR BAY' b'INLAND' b'NEAR BAY'\n",
      " b'INLAND' b'NEAR BAY' b'NEAR BAY' b'NEAR BAY' b'NEAR BAY' b'INLAND'\n",
      " b'INLAND' b'INLAND' b'NEAR BAY' b'INLAND' b'INLAND' b'NEAR BAY'\n",
      " b'NEAR BAY' b'INLAND' b'INLAND' b'INLAND' b'NEAR BAY' b'NEAR BAY'\n",
      " b'INLAND' b'INLAND'], shape=(32,), dtype=string)\n",
      "A batch of targets: tf.Tensor(\n",
      "[260300 205600 229100 178300  62500  63400  69100 118500  66900 274500\n",
      "  85500 342200 118100 290100 208900 263300 175000  69300 109900  55100\n",
      "  75000 119300  74700 270900  69800  62800  84500  53900 239600 350000\n",
      "  43300 141200], shape=(32,), dtype=int64)\n"
     ]
    }
   ],
   "source": [
    "# TODO 1b -- Your code here\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "OT5N6Se-NQsC"
   },
   "source": [
    "We can see that the dataset returns a dictionary of column names (from the dataframe) that map to column values from rows in the dataframe."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "YEGEAqaziwfC"
   },
   "source": [
    "#### Numeric columns\n",
    "The output of a feature column becomes the input to the model. A numeric is the simplest type of column. It is used to represent real valued features. When using this column, your model will receive the column value from the dataframe unchanged.\n",
    "\n",
    "In the California housing prices dataset, most columns from the dataframe are numeric.  Let' create a variable called **numeric_cols** to hold only the numerical feature columns."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "# TODO 1c -- Your code here\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "EwMEcH_52JT8"
   },
   "source": [
    "#### Scaler function\n",
    "It is very important for numerical variables to get scaled before they are \"fed\" into the neural network. Here we use min-max scaling. Here we are creating a function named 'get_scal' which takes a list of numerical features and returns a 'minmax' function, which will be used in tf.feature_column.numeric_column() as normalizer_fn in parameters. 'Minmax' function itself takes a 'numerical' number from a particular feature and return scaled value of that number. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "ig1k5ovWBnN8"
   },
   "source": [
    "Next, we scale the numerical feature columns that we assigned to the variable \"numeric cols\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Scalar def get_scal(feature):\n",
    "# TODO 1d -- Your code here\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "Y8IUfcuVaS_g"
   },
   "outputs": [],
   "source": [
    "# TODO 1e -- Your code here\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "8v9XoD7WCKRM"
   },
   "source": [
    "Next, we should validate the total number of feature columns.  Compare this number to the number of numeric features you input earlier."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "id": "4jgPFThi50sS",
    "outputId": "23ede6f5-a62a-4767-b3a6-fe8a3b89a212"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total number of feature coLumns:  8\n"
     ]
    }
   ],
   "source": [
    "print('Total number of feature coLumns: ', len(feature_columns))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "9Ug3hB8Sl0jO"
   },
   "source": [
    "### Using the Keras Sequential Model\n",
    "\n",
    "Next, we will run this cell to compile and fit the Keras Sequential model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 1000
    },
    "colab_type": "code",
    "id": "_YJPPb3xTPeZ",
    "outputId": "2d445722-1d43-4a27-a6c0-c6ce813ab450"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/32\n",
      "WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'longitude': <tf.Tensor 'ExpandDims_3:0' shape=(None, 1) dtype=float64>, 'latitude': <tf.Tensor 'ExpandDims_2:0' shape=(None, 1) dtype=float64>, 'housing_median_age': <tf.Tensor 'ExpandDims_1:0' shape=(None, 1) dtype=int64>, 'total_rooms': <tf.Tensor 'ExpandDims_8:0' shape=(None, 1) dtype=int64>, 'total_bedrooms': <tf.Tensor 'ExpandDims_7:0' shape=(None, 1) dtype=int64>, 'population': <tf.Tensor 'ExpandDims_6:0' shape=(None, 1) dtype=int64>, 'households': <tf.Tensor 'ExpandDims:0' shape=(None, 1) dtype=int64>, 'median_income': <tf.Tensor 'ExpandDims_4:0' shape=(None, 1) dtype=float64>, 'ocean_proximity': <tf.Tensor 'ExpandDims_5:0' shape=(None, 1) dtype=string>}\n",
      "Consider rewriting this model with the Functional API.\n",
      "WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'longitude': <tf.Tensor 'ExpandDims_3:0' shape=(None, 1) dtype=float64>, 'latitude': <tf.Tensor 'ExpandDims_2:0' shape=(None, 1) dtype=float64>, 'housing_median_age': <tf.Tensor 'ExpandDims_1:0' shape=(None, 1) dtype=int64>, 'total_rooms': <tf.Tensor 'ExpandDims_8:0' shape=(None, 1) dtype=int64>, 'total_bedrooms': <tf.Tensor 'ExpandDims_7:0' shape=(None, 1) dtype=int64>, 'population': <tf.Tensor 'ExpandDims_6:0' shape=(None, 1) dtype=int64>, 'households': <tf.Tensor 'ExpandDims:0' shape=(None, 1) dtype=int64>, 'median_income': <tf.Tensor 'ExpandDims_4:0' shape=(None, 1) dtype=float64>, 'ocean_proximity': <tf.Tensor 'ExpandDims_5:0' shape=(None, 1) dtype=string>}\n",
      "Consider rewriting this model with the Functional API.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2021-12-27 07:28:07.208447: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:185] None of the MLIR Optimization Passes are enabled (registered 2)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "36/50 [====================>.........] - ETA: 0s - loss: 38727671808.0000 - mse: 38727671808.0000WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'longitude': <tf.Tensor 'ExpandDims_3:0' shape=(None, 1) dtype=float64>, 'latitude': <tf.Tensor 'ExpandDims_2:0' shape=(None, 1) dtype=float64>, 'housing_median_age': <tf.Tensor 'ExpandDims_1:0' shape=(None, 1) dtype=int64>, 'total_rooms': <tf.Tensor 'ExpandDims_8:0' shape=(None, 1) dtype=int64>, 'total_bedrooms': <tf.Tensor 'ExpandDims_7:0' shape=(None, 1) dtype=int64>, 'population': <tf.Tensor 'ExpandDims_6:0' shape=(None, 1) dtype=int64>, 'households': <tf.Tensor 'ExpandDims:0' shape=(None, 1) dtype=int64>, 'median_income': <tf.Tensor 'ExpandDims_4:0' shape=(None, 1) dtype=float64>, 'ocean_proximity': <tf.Tensor 'ExpandDims_5:0' shape=(None, 1) dtype=string>}\n",
      "Consider rewriting this model with the Functional API.\n",
      "50/50 [==============================] - 1s 11ms/step - loss: 39193808896.0000 - mse: 39193808896.0000 - val_loss: 36448071680.0000 - val_mse: 36448071680.0000\n",
      "Epoch 2/32\n",
      "50/50 [==============================] - 0s 4ms/step - loss: 39049510912.0000 - mse: 39049510912.0000 - val_loss: 36400619520.0000 - val_mse: 36400619520.0000\n",
      "Epoch 3/32\n",
      "50/50 [==============================] - 0s 6ms/step - loss: 39030571008.0000 - mse: 39030571008.0000 - val_loss: 36396724224.0000 - val_mse: 36396724224.0000\n",
      "Epoch 4/32\n",
      "50/50 [==============================] - 0s 6ms/step - loss: 39028142080.0000 - mse: 39028142080.0000 - val_loss: 36395610112.0000 - val_mse: 36395610112.0000\n",
      "Epoch 5/32\n",
      "50/50 [==============================] - 0s 7ms/step - loss: 39025623040.0000 - mse: 39025623040.0000 - val_loss: 36382359552.0000 - val_mse: 36382359552.0000\n",
      "Epoch 6/32\n",
      "50/50 [==============================] - 0s 6ms/step - loss: 38938464256.0000 - mse: 38938464256.0000 - val_loss: 36213567488.0000 - val_mse: 36213567488.0000\n",
      "Epoch 7/32\n",
      "50/50 [==============================] - 0s 5ms/step - loss: 38668693504.0000 - mse: 38668693504.0000 - val_loss: 35840221184.0000 - val_mse: 35840221184.0000\n",
      "Epoch 8/32\n",
      "50/50 [==============================] - 0s 5ms/step - loss: 38086979584.0000 - mse: 38086979584.0000 - val_loss: 35088863232.0000 - val_mse: 35088863232.0000\n",
      "Epoch 9/32\n",
      "50/50 [==============================] - 0s 6ms/step - loss: 37044961280.0000 - mse: 37044961280.0000 - val_loss: 33850447872.0000 - val_mse: 33850447872.0000\n",
      "Epoch 10/32\n",
      "50/50 [==============================] - 0s 5ms/step - loss: 35404480512.0000 - mse: 35404480512.0000 - val_loss: 31823136768.0000 - val_mse: 31823136768.0000\n",
      "Epoch 11/32\n",
      "50/50 [==============================] - 0s 6ms/step - loss: 32719636480.0000 - mse: 32719636480.0000 - val_loss: 28622012416.0000 - val_mse: 28622012416.0000\n",
      "Epoch 12/32\n",
      "50/50 [==============================] - 0s 5ms/step - loss: 28887259136.0000 - mse: 28887259136.0000 - val_loss: 24609959936.0000 - val_mse: 24609959936.0000\n",
      "Epoch 13/32\n",
      "50/50 [==============================] - 0s 4ms/step - loss: 24745758720.0000 - mse: 24745758720.0000 - val_loss: 20495147008.0000 - val_mse: 20495147008.0000\n",
      "Epoch 14/32\n",
      "50/50 [==============================] - 0s 4ms/step - loss: 21050302464.0000 - mse: 21050302464.0000 - val_loss: 17123078144.0000 - val_mse: 17123078144.0000\n",
      "Epoch 15/32\n",
      "50/50 [==============================] - 0s 4ms/step - loss: 18506897408.0000 - mse: 18506897408.0000 - val_loss: 14988767232.0000 - val_mse: 14988767232.0000\n",
      "Epoch 16/32\n",
      "50/50 [==============================] - 0s 4ms/step - loss: 17230487552.0000 - mse: 17230487552.0000 - val_loss: 13805082624.0000 - val_mse: 13805082624.0000\n",
      "Epoch 17/32\n",
      "50/50 [==============================] - 0s 4ms/step - loss: 16694388736.0000 - mse: 16694388736.0000 - val_loss: 13363025920.0000 - val_mse: 13363025920.0000\n",
      "Epoch 18/32\n",
      "50/50 [==============================] - 0s 4ms/step - loss: 16523415552.0000 - mse: 16523415552.0000 - val_loss: 13121990656.0000 - val_mse: 13121990656.0000\n",
      "Epoch 19/32\n",
      "50/50 [==============================] - 0s 4ms/step - loss: 16460402688.0000 - mse: 16460402688.0000 - val_loss: 12996168704.0000 - val_mse: 12996168704.0000\n",
      "Epoch 20/32\n",
      "50/50 [==============================] - 0s 4ms/step - loss: 16409079808.0000 - mse: 16409079808.0000 - val_loss: 12932403200.0000 - val_mse: 12932403200.0000\n",
      "Epoch 21/32\n",
      "50/50 [==============================] - 0s 5ms/step - loss: 16363088896.0000 - mse: 16363088896.0000 - val_loss: 12857910272.0000 - val_mse: 12857910272.0000\n",
      "Epoch 22/32\n",
      "50/50 [==============================] - 0s 4ms/step - loss: 16321121280.0000 - mse: 16321121280.0000 - val_loss: 12851463168.0000 - val_mse: 12851463168.0000\n",
      "Epoch 23/32\n",
      "50/50 [==============================] - 0s 4ms/step - loss: 16282628096.0000 - mse: 16282628096.0000 - val_loss: 12810982400.0000 - val_mse: 12810982400.0000\n",
      "Epoch 24/32\n",
      "50/50 [==============================] - 0s 5ms/step - loss: 16230232064.0000 - mse: 16230232064.0000 - val_loss: 12769296384.0000 - val_mse: 12769296384.0000\n",
      "Epoch 25/32\n",
      "50/50 [==============================] - 0s 4ms/step - loss: 16181005312.0000 - mse: 16181005312.0000 - val_loss: 12725964800.0000 - val_mse: 12725964800.0000\n",
      "Epoch 26/32\n",
      "50/50 [==============================] - 0s 5ms/step - loss: 16133206016.0000 - mse: 16133206016.0000 - val_loss: 12675289088.0000 - val_mse: 12675289088.0000\n",
      "Epoch 27/32\n",
      "50/50 [==============================] - 0s 5ms/step - loss: 16090496000.0000 - mse: 16090496000.0000 - val_loss: 12608101376.0000 - val_mse: 12608101376.0000\n",
      "Epoch 28/32\n",
      "50/50 [==============================] - 0s 4ms/step - loss: 16028748800.0000 - mse: 16028748800.0000 - val_loss: 12621775872.0000 - val_mse: 12621775872.0000\n",
      "Epoch 29/32\n",
      "50/50 [==============================] - 0s 4ms/step - loss: 15993242624.0000 - mse: 15993242624.0000 - val_loss: 12573082624.0000 - val_mse: 12573082624.0000\n",
      "Epoch 30/32\n",
      "50/50 [==============================] - 0s 4ms/step - loss: 15927695360.0000 - mse: 15927695360.0000 - val_loss: 12529428480.0000 - val_mse: 12529428480.0000\n",
      "Epoch 31/32\n",
      "50/50 [==============================] - 0s 5ms/step - loss: 15872490496.0000 - mse: 15872490496.0000 - val_loss: 12457719808.0000 - val_mse: 12457719808.0000\n",
      "Epoch 32/32\n",
      "50/50 [==============================] - 0s 5ms/step - loss: 15834402816.0000 - mse: 15834402816.0000 - val_loss: 12434145280.0000 - val_mse: 12434145280.0000\n"
     ]
    }
   ],
   "source": [
    "# Model create\n",
    "feature_layer = tf.keras.layers.DenseFeatures(feature_columns, dtype='float64')\n",
    "\n",
    "model = tf.keras.Sequential([\n",
    "  feature_layer,\n",
    "  layers.Dense(12, input_dim=8, activation='relu'),\n",
    "  layers.Dense(8, activation='relu'),\n",
    "  layers.Dense(1, activation='linear',  name='median_house_value')\n",
    "])\n",
    "\n",
    "# Model compile\n",
    "model.compile(optimizer='adam',\n",
    "              loss='mse',\n",
    "              metrics=['mse'])\n",
    "\n",
    "# Model Fit\n",
    "history = model.fit(train_ds,\n",
    "                    validation_data=val_ds,\n",
    "                    epochs=32)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next we show loss as  Mean Square Error (MSE).  Remember that MSE is the most commonly used regression loss function. MSE is the sum of squared distances between our target variable (e.g. housing median age) and predicted values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 71
    },
    "colab_type": "code",
    "id": "vo7hhkPqm6Jx",
    "outputId": "938907f6-b6c8-497c-a8f6-0f1cdbf336c9"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "50/50 [==============================] - 0s 3ms/step - loss: 15769783296.0000 - mse: 15769783296.0000\n",
      "Mean Squared Error 15769783296.0\n"
     ]
    }
   ],
   "source": [
    "loss, mse = model.evaluate(train_ds)\n",
    "print(\"Mean Squared Error\", mse)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "252EPxGp7-FJ"
   },
   "source": [
    "#### Visualize the model loss curve\n",
    "\n",
    "Next, we will use matplotlib to draw the model's loss curves for training and validation.  A line plot is also created showing the mean squared error loss over the training epochs for both the train (blue) and test (orange) sets."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_curves(history, metrics):\n",
    "    nrows = 1\n",
    "    ncols = 2\n",
    "    fig = plt.figure(figsize=(10, 5))\n",
    "\n",
    "    for idx, key in enumerate(metrics):  \n",
    "        ax = fig.add_subplot(nrows, ncols, idx+1)\n",
    "        plt.plot(history.history[key])\n",
    "        plt.plot(history.history['val_{}'.format(key)])\n",
    "        plt.title('model {}'.format(key))\n",
    "        plt.ylabel(key)\n",
    "        plt.xlabel('epoch')\n",
    "        plt.legend(['train', 'validation'], loc='upper left');  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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en8zo7I4m4xQgF0Hu49LYA8//DBFRUUxiFRE5HhTzWGF2WFK8zM51NXPoa5+5TCZ9VSEzYBPf/rT3H777aM/f/azn+Vzn/scl156KStXruT000/ngQceOO5zO3bsoLa2FoCenh6uu+46VqxYwbXXXnvUfdc++tGPsmrVKmpqavj85z7HwpIcfvTf36SpqYmLL76Yiy++GIBFixZx4MABAL785S9TW1tLbW0tX/3qV0d/3/Lly7n55pupqanhsssu0/3dRCSuOeybX/pXcjND/MeXvqIcJuKcS6nHGWec4Y61adOm496LpzVr1rgLL7xw9Ofly5e7nTt3ura2Nueccy0tLe6kk05yw8PDzjnncnNznXPObd++3dXU1DjnnPvSl77kbrrpJuecc+vWrXPBYNC9+OKLzjnnDh486JxzbnBw0L35zW9269atc4e7+lxFZZV7Zeuu0d+7cOFC19LS4lavXu1qa2tdZ2en6+jocKeddppbs2aN2759uwsGg+7ll192zjn3vve9z/3oRz8a98+V6O9VZASw2iVB/pmJh3LYm93qNS+7TU1trqKy2u3dv3/0985kDkv0dyoyYqL8lX73jvzlbbDvlZk95vzT4covjLu5oaGB5uZmmpqaaGlpobi4mPLycj75yU/y1FNPEQgE2LNnD/v372f+/PkRj/HUU0/x8Y9/HIAVK1awYsWK0W333HMPd9xxB4ODg+zdu5dNmzaxYsUKAmYc7OqnrbufwpyM0f2feeYZ3v3ud5ObmwvAe97zHp5++mne+c53snjxYurr6wE444wz2LFjxwl+OSIyo2ZBDtvy2qu847RawNF0uId5pe6oWxwph8lskX5FWIJcc8013Hvvvezbt4/rrruOu+66i5aWFl566SXC4TCLFi2it7d3wmNEus/a9u3b+eIXv8iLL75IcXExN9544+hxggEjOxyk8XAPmeHg6GfcBEtYZGYeubluMBjUVL6IAPHPYbmZIYIBo6NvkJbOPsrys0Y/oxwms0X6FWETjPZi6brrruPmm2/mwIED/Pa3v+Wee+6hrKyMcDjME088wc6dOyf8/IUXXshdd93FxRdfzIYNG1i/fj0A7e3t5ObmUlhYyP79+/nlL3/JRRddBEB+fj6FoSGGzNh1qPuoY914443cdtttOOe4//77+dGPfhSzP7uIzKBZlMMKCwoIDvay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",
      "text/plain": [
       "<Figure size 720x360 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_curves(history, ['loss', 'mse'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "wqkozY268xi7"
   },
   "source": [
    "### Load test data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "uf4TyVJ_Dzxe"
   },
   "source": [
    "Next, we read in the test.csv file and validate that there are no null values.  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Again, we can use .describe() to see some summary statistics for the numeric fields in our dataframe.  The count shows 500.000000 for all feature columns. Thus, there are no missing values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 222
    },
    "colab_type": "code",
    "id": "b4C4BmhV8ch9",
    "outputId": "82bcc9d3-4432-4068-ab82-6a6abbe4a024"
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>longitude</th>\n",
       "      <th>latitude</th>\n",
       "      <th>housing_median_age</th>\n",
       "      <th>total_rooms</th>\n",
       "      <th>total_bedrooms</th>\n",
       "      <th>population</th>\n",
       "      <th>households</th>\n",
       "      <th>median_income</th>\n",
       "      <th>median_house_value</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>count</th>\n",
       "      <td>500.000000</td>\n",
       "      <td>500.000000</td>\n",
       "      <td>500.000000</td>\n",
       "      <td>500.000000</td>\n",
       "      <td>500.000000</td>\n",
       "      <td>500.000000</td>\n",
       "      <td>500.000000</td>\n",
       "      <td>500.000000</td>\n",
       "      <td>500.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>mean</th>\n",
       "      <td>-121.484620</td>\n",
       "      <td>37.788780</td>\n",
       "      <td>29.434000</td>\n",
       "      <td>2454.474000</td>\n",
       "      <td>483.734000</td>\n",
       "      <td>1224.256000</td>\n",
       "      <td>451.594000</td>\n",
       "      <td>3.668661</td>\n",
       "      <td>170155.210000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>std</th>\n",
       "      <td>1.021337</td>\n",
       "      <td>0.783415</td>\n",
       "      <td>14.118935</td>\n",
       "      <td>1762.173905</td>\n",
       "      <td>341.648965</td>\n",
       "      <td>825.955172</td>\n",
       "      <td>315.922007</td>\n",
       "      <td>1.884980</td>\n",
       "      <td>98074.219727</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>min</th>\n",
       "      <td>-124.230000</td>\n",
       "      <td>36.140000</td>\n",
       "      <td>2.000000</td>\n",
       "      <td>44.000000</td>\n",
       "      <td>6.000000</td>\n",
       "      <td>23.000000</td>\n",
       "      <td>11.000000</td>\n",
       "      <td>0.536000</td>\n",
       "      <td>39200.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25%</th>\n",
       "      <td>-122.182500</td>\n",
       "      <td>37.607500</td>\n",
       "      <td>17.000000</td>\n",
       "      <td>1431.750000</td>\n",
       "      <td>281.500000</td>\n",
       "      <td>736.750000</td>\n",
       "      <td>269.000000</td>\n",
       "      <td>2.269050</td>\n",
       "      <td>92550.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>50%</th>\n",
       "      <td>-122.020000</td>\n",
       "      <td>37.800000</td>\n",
       "      <td>29.000000</td>\n",
       "      <td>1999.000000</td>\n",
       "      <td>405.000000</td>\n",
       "      <td>995.500000</td>\n",
       "      <td>371.000000</td>\n",
       "      <td>3.250000</td>\n",
       "      <td>151400.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>75%</th>\n",
       "      <td>-120.620000</td>\n",
       "      <td>37.960000</td>\n",
       "      <td>40.000000</td>\n",
       "      <td>3024.250000</td>\n",
       "      <td>576.750000</td>\n",
       "      <td>1484.500000</td>\n",
       "      <td>522.500000</td>\n",
       "      <td>4.753050</td>\n",
       "      <td>218625.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>max</th>\n",
       "      <td>-118.940000</td>\n",
       "      <td>41.750000</td>\n",
       "      <td>52.000000</td>\n",
       "      <td>14917.000000</td>\n",
       "      <td>2708.000000</td>\n",
       "      <td>8012.000000</td>\n",
       "      <td>2606.000000</td>\n",
       "      <td>12.591500</td>\n",
       "      <td>500001.000000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        longitude    latitude  housing_median_age   total_rooms  \\\n",
       "count  500.000000  500.000000          500.000000    500.000000   \n",
       "mean  -121.484620   37.788780           29.434000   2454.474000   \n",
       "std      1.021337    0.783415           14.118935   1762.173905   \n",
       "min   -124.230000   36.140000            2.000000     44.000000   \n",
       "25%   -122.182500   37.607500           17.000000   1431.750000   \n",
       "50%   -122.020000   37.800000           29.000000   1999.000000   \n",
       "75%   -120.620000   37.960000           40.000000   3024.250000   \n",
       "max   -118.940000   41.750000           52.000000  14917.000000   \n",
       "\n",
       "       total_bedrooms   population   households  median_income  \\\n",
       "count      500.000000   500.000000   500.000000     500.000000   \n",
       "mean       483.734000  1224.256000   451.594000       3.668661   \n",
       "std        341.648965   825.955172   315.922007       1.884980   \n",
       "min          6.000000    23.000000    11.000000       0.536000   \n",
       "25%        281.500000   736.750000   269.000000       2.269050   \n",
       "50%        405.000000   995.500000   371.000000       3.250000   \n",
       "75%        576.750000  1484.500000   522.500000       4.753050   \n",
       "max       2708.000000  8012.000000  2606.000000      12.591500   \n",
       "\n",
       "       median_house_value  \n",
       "count          500.000000  \n",
       "mean        170155.210000  \n",
       "std          98074.219727  \n",
       "min          39200.000000  \n",
       "25%          92550.000000  \n",
       "50%         151400.000000  \n",
       "75%         218625.000000  \n",
       "max         500001.000000  "
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "test_data = pd.read_csv('../data/housing-test.csv')\n",
    "test_data.describe()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "nY2Yrt8fC7RW"
   },
   "source": [
    "Now that we have created an input pipeline using tf.data and compiled a Keras Sequential Model, we now create the input function for the test data and to initialize the test_predict variable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "8rMdDeGDCwpT"
   },
   "outputs": [],
   "source": [
    "# TODO 1f -- Your code here\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_predict = test_input_fn(dict(test_data))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "H5SkINtbDIdr"
   },
   "source": [
    "#### Prediction:  Linear Regression\n",
    "\n",
    "Before we begin to feature engineer our feature columns, we should predict the median house value.  By predicting the median house value now, we can then compare it with the median house value after feature engineering.\n",
    "\n",
    "To predict with Keras, you simply call [model.predict()](https://keras.io/models/model/#predict) and pass in the housing features you want to predict the median_house_value for. Note:  We are predicting the model locally."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "uNc6TSoJDL7-"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'longitude': <tf.Tensor 'ExpandDims_3:0' shape=(None, 1) dtype=float64>, 'latitude': <tf.Tensor 'ExpandDims_2:0' shape=(None, 1) dtype=float64>, 'housing_median_age': <tf.Tensor 'ExpandDims_1:0' shape=(None, 1) dtype=int64>, 'total_rooms': <tf.Tensor 'ExpandDims_9:0' shape=(None, 1) dtype=int64>, 'total_bedrooms': <tf.Tensor 'ExpandDims_8:0' shape=(None, 1) dtype=int64>, 'population': <tf.Tensor 'ExpandDims_7:0' shape=(None, 1) dtype=int64>, 'households': <tf.Tensor 'ExpandDims:0' shape=(None, 1) dtype=int64>, 'median_income': <tf.Tensor 'ExpandDims_5:0' shape=(None, 1) dtype=float64>, 'median_house_value': <tf.Tensor 'ExpandDims_4:0' shape=(None, 1) dtype=int64>, 'ocean_proximity': <tf.Tensor 'ExpandDims_6:0' shape=(None, 1) dtype=string>}\n",
      "Consider rewriting this model with the Functional API.\n"
     ]
    }
   ],
   "source": [
    "predicted_median_house_value = model.predict(test_predict)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "HFXK1SKPDYgD"
   },
   "source": [
    "Next, we run two predictions in separate cells - one where ocean_proximity=INLAND and one where ocean_proximity= NEAR OCEAN. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "id": "xepss0vhoHge",
    "outputId": "46842a26-eacd-4801-857b-18c6a8f2005c"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'longitude': <tf.Tensor 'ExpandDims_3:0' shape=(1, 1) dtype=float32>, 'latitude': <tf.Tensor 'ExpandDims_2:0' shape=(1, 1) dtype=float32>, 'housing_median_age': <tf.Tensor 'ExpandDims_1:0' shape=(1, 1) dtype=float32>, 'total_rooms': <tf.Tensor 'ExpandDims_8:0' shape=(1, 1) dtype=float32>, 'total_bedrooms': <tf.Tensor 'ExpandDims_7:0' shape=(1, 1) dtype=float32>, 'population': <tf.Tensor 'ExpandDims_6:0' shape=(1, 1) dtype=float32>, 'households': <tf.Tensor 'ExpandDims:0' shape=(1, 1) dtype=float32>, 'median_income': <tf.Tensor 'ExpandDims_4:0' shape=(1, 1) dtype=float32>, 'ocean_proximity': <tf.Tensor 'ExpandDims_5:0' shape=(1, 1) dtype=string>}\n",
      "Consider rewriting this model with the Functional API.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([[343034.]], dtype=float32)"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Ocean_proximity is INLAND\n",
    "model.predict({\n",
    "    'longitude': tf.convert_to_tensor([-121.86]),\n",
    "    'latitude': tf.convert_to_tensor([39.78]),\n",
    "    'housing_median_age': tf.convert_to_tensor([12.0]),\n",
    "    'total_rooms': tf.convert_to_tensor([7653.0]),\n",
    "    'total_bedrooms': tf.convert_to_tensor([1578.0]),\n",
    "    'population': tf.convert_to_tensor([3628.0]),\n",
    "    'households': tf.convert_to_tensor([1494.0]),\n",
    "    'median_income': tf.convert_to_tensor([3.0905]),\n",
    "    'ocean_proximity': tf.convert_to_tensor(['INLAND'])\n",
    "}, steps=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "id": "qPssm8p4EZHh",
    "outputId": "2a55d427-7857-401c-f60d-edbb36be19ec"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[190023.45]], dtype=float32)"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Ocean_proximity is NEAR OCEAN\n",
    "model.predict({\n",
    "    'longitude': tf.convert_to_tensor([-122.43]),\n",
    "    'latitude': tf.convert_to_tensor([37.63]),\n",
    "    'housing_median_age': tf.convert_to_tensor([34.0]),\n",
    "    'total_rooms': tf.convert_to_tensor([4135.0]),\n",
    "    'total_bedrooms': tf.convert_to_tensor([687.0]),\n",
    "    'population': tf.convert_to_tensor([2154.0]),\n",
    "    'households': tf.convert_to_tensor([742.0]),\n",
    "    'median_income': tf.convert_to_tensor([4.9732]),\n",
    "    'ocean_proximity': tf.convert_to_tensor(['NEAR OCEAN'])\n",
    "}, steps=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Txl-MRuLFE_8"
   },
   "source": [
    "The arrays returns a predicted value.  What do these numbers mean?  Let's compare this value to the test set.  \n",
    "\n",
    "Go to the test.csv you read in a few cells up.  Locate the first line and find the median_house_value - which should be 249,000 dollars near the ocean. What value did your model predicted for the median_house_value? Was it a solid model performance? Let's see if we can improve this a bit with feature engineering!  \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lab Task 2: Engineer features to create categorical and numerical features"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "78F1XH1Qwvbt"
   },
   "source": [
    "Now we create a cell that indicates which features will be used in the model.  \n",
    "Note:  Be sure to bucketize 'housing_median_age' and ensure that 'ocean_proximity' is one-hot encoded.  And, don't forget your numeric values!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "ZxSatLUxUmvI"
   },
   "outputs": [],
   "source": [
    "# TODO 2a -- Your code here\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "5HbypkYHxxwt"
   },
   "source": [
    "Next, we scale the numerical, bucktized, and categorical feature columns that we assigned to the variables in the preceding cell."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "ExX5Akz0UnE-"
   },
   "outputs": [],
   "source": [
    "# Scalar def get_scal(feature):\n",
    "def get_scal(feature):\n",
    "    def minmax(x):\n",
    "        mini = train[feature].min()\n",
    "        maxi = train[feature].max()\n",
    "        return (x - mini)/(maxi-mini)\n",
    "        return(minmax)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "wzqcddUQUnKn"
   },
   "outputs": [],
   "source": [
    "# All numerical features - scaling\n",
    "feature_columns = []\n",
    "for header in numeric_cols:\n",
    "    scal_input_fn = get_scal(header)\n",
    "    feature_columns.append(fc.numeric_column(header,\n",
    "                                             normalizer_fn=scal_input_fn))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "yYUpUZvgwrPe"
   },
   "source": [
    "### Categorical Feature\n",
    "In this dataset, 'ocean_proximity' is represented as a string.  We cannot feed strings directly to a model. Instead, we must first map them to numeric values. The categorical vocabulary columns provide a way to represent strings as a one-hot vector."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "sZnlnFZkyEbe"
   },
   "source": [
    "Next, we create a categorical feature using 'ocean_proximity'."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "3Cf6SoFTUnc6"
   },
   "outputs": [],
   "source": [
    "# TODO 2b -- Your code here\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "qnGyWaijzShj"
   },
   "source": [
    "### Bucketized Feature\n",
    "\n",
    "Often, you don't want to feed a number directly into the model, but instead split its value into different categories based on numerical ranges. Consider our raw data that represents a homes' age. Instead of representing the house age as a numeric column, we could split the home age into several buckets using a [bucketized column](https://www.tensorflow.org/api_docs/python/tf/feature_column/bucketized_column). Notice the one-hot values below describe which age range each row matches."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "7ZRlFyP7fOw-"
   },
   "source": [
    "Next we create a bucketized column using 'housing_median_age'\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "xB-yiVLmUnXp"
   },
   "outputs": [],
   "source": [
    "# TODO 2c -- Your code here\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Ri4_wssOg943"
   },
   "source": [
    "### Feature Cross\n",
    "\n",
    "Combining features into a single feature, better known as [feature crosses](https://developers.google.com/machine-learning/glossary/#feature_cross), enables a model to learn separate weights for each combination of features."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "a6HHJl3J0j0T"
   },
   "source": [
    "Next, we create a feature cross of 'housing_median_age' and 'ocean_proximity'."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "JVLnG0WbUnkl"
   },
   "outputs": [],
   "source": [
    "# TODO 2d -- Your code here\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "hiz6HCWg1CXO"
   },
   "source": [
    "Next, we should validate the total number of feature columns.  Compare this number to the number of numeric features you input earlier."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "id": "6P3Ewc3_Unsv",
    "outputId": "42c1c4a6-89f8-4685-b2d0-e76a90cdf9ee"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total number of feature columns:  11\n"
     ]
    }
   ],
   "source": [
    "print('Total number of feature columns: ', len(feature_columns))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "lNr00mP41sJp"
   },
   "source": [
    "Next, we will run this cell to compile and fit the Keras Sequential model.  This is the same model we ran earlier."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 1000
    },
    "colab_type": "code",
    "id": "4Dwal3oxUoCe",
    "outputId": "1ae08747-7dbe-47a5-b3e7-87581e460b1b"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/32\n",
      "WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'longitude': <tf.Tensor 'ExpandDims_3:0' shape=(None, 1) dtype=float64>, 'latitude': <tf.Tensor 'ExpandDims_2:0' shape=(None, 1) dtype=float64>, 'housing_median_age': <tf.Tensor 'ExpandDims_1:0' shape=(None, 1) dtype=int64>, 'total_rooms': <tf.Tensor 'ExpandDims_8:0' shape=(None, 1) dtype=int64>, 'total_bedrooms': <tf.Tensor 'ExpandDims_7:0' shape=(None, 1) dtype=int64>, 'population': <tf.Tensor 'ExpandDims_6:0' shape=(None, 1) dtype=int64>, 'households': <tf.Tensor 'ExpandDims:0' shape=(None, 1) dtype=int64>, 'median_income': <tf.Tensor 'ExpandDims_4:0' shape=(None, 1) dtype=float64>, 'ocean_proximity': <tf.Tensor 'ExpandDims_5:0' shape=(None, 1) dtype=string>}\n",
      "Consider rewriting this model with the Functional API.\n",
      "WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'longitude': <tf.Tensor 'ExpandDims_3:0' shape=(None, 1) dtype=float64>, 'latitude': <tf.Tensor 'ExpandDims_2:0' shape=(None, 1) dtype=float64>, 'housing_median_age': <tf.Tensor 'ExpandDims_1:0' shape=(None, 1) dtype=int64>, 'total_rooms': <tf.Tensor 'ExpandDims_8:0' shape=(None, 1) dtype=int64>, 'total_bedrooms': <tf.Tensor 'ExpandDims_7:0' shape=(None, 1) dtype=int64>, 'population': <tf.Tensor 'ExpandDims_6:0' shape=(None, 1) dtype=int64>, 'households': <tf.Tensor 'ExpandDims:0' shape=(None, 1) dtype=int64>, 'median_income': <tf.Tensor 'ExpandDims_4:0' shape=(None, 1) dtype=float64>, 'ocean_proximity': <tf.Tensor 'ExpandDims_5:0' shape=(None, 1) dtype=string>}\n",
      "Consider rewriting this model with the Functional API.\n",
      "48/50 [===========================>..] - ETA: 0s - loss: 39152873472.0000 - mse: 39152873472.0000WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'longitude': <tf.Tensor 'ExpandDims_3:0' shape=(None, 1) dtype=float64>, 'latitude': <tf.Tensor 'ExpandDims_2:0' shape=(None, 1) dtype=float64>, 'housing_median_age': <tf.Tensor 'ExpandDims_1:0' shape=(None, 1) dtype=int64>, 'total_rooms': <tf.Tensor 'ExpandDims_8:0' shape=(None, 1) dtype=int64>, 'total_bedrooms': <tf.Tensor 'ExpandDims_7:0' shape=(None, 1) dtype=int64>, 'population': <tf.Tensor 'ExpandDims_6:0' shape=(None, 1) dtype=int64>, 'households': <tf.Tensor 'ExpandDims:0' shape=(None, 1) dtype=int64>, 'median_income': <tf.Tensor 'ExpandDims_4:0' shape=(None, 1) dtype=float64>, 'ocean_proximity': <tf.Tensor 'ExpandDims_5:0' shape=(None, 1) dtype=string>}\n",
      "Consider rewriting this model with the Functional API.\n",
      "50/50 [==============================] - 1s 14ms/step - loss: 39040045056.0000 - mse: 39040045056.0000 - val_loss: 36382720000.0000 - val_mse: 36382720000.0000\n",
      "Epoch 2/32\n",
      "50/50 [==============================] - 0s 5ms/step - loss: 38990188544.0000 - mse: 38990188544.0000 - val_loss: 36328767488.0000 - val_mse: 36328767488.0000\n",
      "Epoch 3/32\n",
      "50/50 [==============================] - 0s 5ms/step - loss: 38887571456.0000 - mse: 38887571456.0000 - val_loss: 36164743168.0000 - val_mse: 36164743168.0000\n",
      "Epoch 4/32\n",
      "50/50 [==============================] - 1s 14ms/step - loss: 38515343360.0000 - mse: 38515343360.0000 - val_loss: 35509383168.0000 - val_mse: 35509383168.0000\n",
      "Epoch 5/32\n",
      "50/50 [==============================] - 0s 6ms/step - loss: 37387968512.0000 - mse: 37387968512.0000 - val_loss: 34007154688.0000 - val_mse: 34007154688.0000\n",
      "Epoch 6/32\n",
      "50/50 [==============================] - 1s 12ms/step - loss: 35297824768.0000 - mse: 35297824768.0000 - val_loss: 31501572096.0000 - val_mse: 31501572096.0000\n",
      "Epoch 7/32\n",
      "50/50 [==============================] - 0s 6ms/step - loss: 32232638464.0000 - mse: 32232638464.0000 - val_loss: 28151220224.0000 - val_mse: 28151220224.0000\n",
      "Epoch 8/32\n",
      "50/50 [==============================] - 0s 8ms/step - loss: 28477151232.0000 - mse: 28477151232.0000 - val_loss: 24276324352.0000 - val_mse: 24276324352.0000\n",
      "Epoch 9/32\n",
      "50/50 [==============================] - 1s 19ms/step - loss: 24563978240.0000 - mse: 24563978240.0000 - val_loss: 20516687872.0000 - val_mse: 20516687872.0000\n",
      "Epoch 10/32\n",
      "50/50 [==============================] - 1s 13ms/step - loss: 21183547392.0000 - mse: 21183547392.0000 - val_loss: 17380229120.0000 - val_mse: 17380229120.0000\n",
      "Epoch 11/32\n",
      "50/50 [==============================] - 1s 16ms/step - loss: 18815739904.0000 - mse: 18815739904.0000 - val_loss: 15276138496.0000 - val_mse: 15276138496.0000\n",
      "Epoch 12/32\n",
      "50/50 [==============================] - 0s 9ms/step - loss: 17489272832.0000 - mse: 17489272832.0000 - val_loss: 14149625856.0000 - val_mse: 14149625856.0000\n",
      "Epoch 13/32\n",
      "50/50 [==============================] - 0s 9ms/step - loss: 16900322304.0000 - mse: 16900322304.0000 - val_loss: 13555203072.0000 - val_mse: 13555203072.0000\n",
      "Epoch 14/32\n",
      "50/50 [==============================] - 1s 11ms/step - loss: 16682143744.0000 - mse: 16682143744.0000 - val_loss: 13272046592.0000 - val_mse: 13272046592.0000\n",
      "Epoch 15/32\n",
      "50/50 [==============================] - 0s 9ms/step - loss: 16586214400.0000 - mse: 16586214400.0000 - val_loss: 13190628352.0000 - val_mse: 13190628352.0000\n",
      "Epoch 16/32\n",
      "50/50 [==============================] - 0s 7ms/step - loss: 16563254272.0000 - mse: 16563254272.0000 - val_loss: 13110885376.0000 - val_mse: 13110885376.0000\n",
      "Epoch 17/32\n",
      "50/50 [==============================] - 1s 10ms/step - loss: 16508491776.0000 - mse: 16508491776.0000 - val_loss: 13069643776.0000 - val_mse: 13069643776.0000\n",
      "Epoch 18/32\n",
      "50/50 [==============================] - 0s 6ms/step - loss: 16496851968.0000 - mse: 16496851968.0000 - val_loss: 12955588608.0000 - val_mse: 12955588608.0000\n",
      "Epoch 19/32\n",
      "50/50 [==============================] - 0s 6ms/step - loss: 16426155008.0000 - mse: 16426155008.0000 - val_loss: 12983805952.0000 - val_mse: 12983805952.0000\n",
      "Epoch 20/32\n",
      "50/50 [==============================] - 1s 12ms/step - loss: 16407000064.0000 - mse: 16407000064.0000 - val_loss: 12907727872.0000 - val_mse: 12907727872.0000\n",
      "Epoch 21/32\n",
      "50/50 [==============================] - 0s 9ms/step - loss: 16369521664.0000 - mse: 16369521664.0000 - val_loss: 12923857920.0000 - val_mse: 12923857920.0000\n",
      "Epoch 22/32\n",
      "50/50 [==============================] - 0s 8ms/step - loss: 16326635520.0000 - mse: 16326635520.0000 - val_loss: 12859561984.0000 - val_mse: 12859561984.0000\n",
      "Epoch 23/32\n",
      "50/50 [==============================] - 0s 8ms/step - loss: 16284325888.0000 - mse: 16284325888.0000 - val_loss: 12824773632.0000 - val_mse: 12824773632.0000\n",
      "Epoch 24/32\n",
      "50/50 [==============================] - 0s 5ms/step - loss: 16250818560.0000 - mse: 16250818560.0000 - val_loss: 12776425472.0000 - val_mse: 12776425472.0000\n",
      "Epoch 25/32\n",
      "50/50 [==============================] - 0s 6ms/step - loss: 16206730240.0000 - mse: 16206730240.0000 - val_loss: 12781531136.0000 - val_mse: 12781531136.0000\n",
      "Epoch 26/32\n",
      "50/50 [==============================] - 0s 9ms/step - loss: 16148877312.0000 - mse: 16148877312.0000 - val_loss: 12709095424.0000 - val_mse: 12709095424.0000\n",
      "Epoch 27/32\n",
      "50/50 [==============================] - 0s 5ms/step - loss: 16105139200.0000 - mse: 16105139200.0000 - val_loss: 12657540096.0000 - val_mse: 12657540096.0000\n",
      "Epoch 28/32\n",
      "50/50 [==============================] - 0s 7ms/step - loss: 16056802304.0000 - mse: 16056802304.0000 - val_loss: 12586595328.0000 - val_mse: 12586595328.0000\n",
      "Epoch 29/32\n",
      "50/50 [==============================] - 0s 7ms/step - loss: 15999619072.0000 - mse: 15999619072.0000 - val_loss: 12592135168.0000 - val_mse: 12592135168.0000\n",
      "Epoch 30/32\n",
      "50/50 [==============================] - 1s 11ms/step - loss: 15941670912.0000 - mse: 15941670912.0000 - val_loss: 12526983168.0000 - val_mse: 12526983168.0000\n",
      "Epoch 31/32\n",
      "50/50 [==============================] - 0s 10ms/step - loss: 15914262528.0000 - mse: 15914262528.0000 - val_loss: 12476542976.0000 - val_mse: 12476542976.0000\n",
      "Epoch 32/32\n",
      "50/50 [==============================] - 0s 7ms/step - loss: 15842671616.0000 - mse: 15842671616.0000 - val_loss: 12427709440.0000 - val_mse: 12427709440.0000\n"
     ]
    }
   ],
   "source": [
    "# Model create\n",
    "feature_layer = tf.keras.layers.DenseFeatures(feature_columns,\n",
    "                                              dtype='float64')\n",
    "\n",
    "model = tf.keras.Sequential([\n",
    "  feature_layer,\n",
    "  layers.Dense(12, input_dim=8, activation='relu'),\n",
    "  layers.Dense(8, activation='relu'),\n",
    "  layers.Dense(1, activation='linear',  name='median_house_value')\n",
    "])\n",
    "\n",
    "# Model compile\n",
    "model.compile(optimizer='adam',\n",
    "              loss='mse',\n",
    "              metrics=['mse'])\n",
    "\n",
    "# Model Fit\n",
    "history = model.fit(train_ds,\n",
    "                    validation_data=val_ds,\n",
    "                    epochs=32)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "3LdUQszM16Oj"
   },
   "source": [
    "Next, we show loss and mean squared error then plot the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 71
    },
    "colab_type": "code",
    "id": "ZtFSpkd9UoAW",
    "outputId": "bac4836e-c4f1-4b29-876d-91fe1b51a5a7"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "50/50 [==============================] - 0s 4ms/step - loss: 15794762752.0000 - mse: 15794762752.0000\n",
      "Mean Squared Error 15794762752.0\n"
     ]
    }
   ],
   "source": [
    "loss, mse = model.evaluate(train_ds)\n",
    "print(\"Mean Squared Error\", mse)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 350
    },
    "colab_type": "code",
    "id": "O8kWMa6xUn-M",
    "outputId": "05ed9323-1102-4245-a40b-88543f11b0f3"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 720x360 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_curves(history, ['loss', 'mse'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "C4tWwOQt2e-P"
   },
   "source": [
    "Next we create a prediction model.  Note:  You may use the same values from the previous prediciton.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'longitude': <tf.Tensor 'ExpandDims_3:0' shape=(1, 1) dtype=float32>, 'latitude': <tf.Tensor 'ExpandDims_2:0' shape=(1, 1) dtype=float32>, 'housing_median_age': <tf.Tensor 'ExpandDims_1:0' shape=(1, 1) dtype=float32>, 'total_rooms': <tf.Tensor 'ExpandDims_8:0' shape=(1, 1) dtype=float32>, 'total_bedrooms': <tf.Tensor 'ExpandDims_7:0' shape=(1, 1) dtype=float32>, 'population': <tf.Tensor 'ExpandDims_6:0' shape=(1, 1) dtype=float32>, 'households': <tf.Tensor 'ExpandDims:0' shape=(1, 1) dtype=float32>, 'median_income': <tf.Tensor 'ExpandDims_4:0' shape=(1, 1) dtype=float32>, 'ocean_proximity': <tf.Tensor 'ExpandDims_5:0' shape=(1, 1) dtype=string>}\n",
      "Consider rewriting this model with the Functional API.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([[192184.52]], dtype=float32)"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# TODO 2e -- Your code here\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "rcbdA3arXkej"
   },
   "source": [
    "### Analysis \n",
    "\n",
    "The array returns a predicted value.  Compare this value to the test set you ran earlier. Your predicted value may be a bit better.\n",
    "\n",
    "Now that you have your \"feature engineering template\" setup, you can experiment by creating additional features.  For example, you can create derived features, such as households per population, and see how they impact the model.  You can also experiment with replacing the features you used to create the feature cross.\n",
    " "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Copyright 2022 Google Inc.\n",
    "Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at\n",
    "http://www.apache.org/licenses/LICENSE-2.0\n",
    "Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License."
   ]
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "name": "Basic Feature Engineering in Keras.ipynb",
   "provenance": [],
   "toc_visible": true
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
